package org.raymond.icloud.common.starter.interceptor;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.raymond.icloud.common.core.exception.BaseException;
import org.raymond.icloud.common.core.model.Result;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.*;
import java.nio.charset.StandardCharsets;

@Slf4j
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
    private RSA rsa;

    public RestTemplateInterceptor(RSA rsa){
        this.rsa = rsa;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
        String method = request.getMethod().name();
        ClientHttpResponse restResponse = null;
        if(StringUtils.equalsIgnoreCase(method, Method.POST.name())) {
            String plain = StrUtil.str(bytes, StandardCharsets.UTF_8);
            String cipher = rsa.encryptBase64(plain, KeyType.PublicKey);
            log.debug("headers: {},\nplain: {},\ncipher: {}", request.getHeaders(), plain, cipher);
            restResponse = execution.execute(request, cipher.getBytes(StandardCharsets.UTF_8));
        }else if(StringUtils.equalsIgnoreCase(method, Method.GET.name())){
            restResponse = execution.execute(request, bytes);
        }else {
            throw new BaseException("System not support method: ["+method+"]");
        }
        byte[] decryptResult = parse(restResponse.getBody(), request.getURI().toString());

        // result为空,则不需要再做解密处理
        if(decryptResult==null) return restResponse;

        InputStream is = new ByteArrayInputStream(decryptResult);

        final ClientHttpResponse response = restResponse;
        return new ClientHttpResponse(){
            @Override
            public HttpHeaders getHeaders() {
                return response.getHeaders();
            }
            @Override
            public InputStream getBody() throws IOException {
                return is;
            }
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return response.getStatusCode();
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return response.getRawStatusCode();
            }
            @Override
            public String getStatusText() throws IOException {
                return response.getStatusText();
            }
            @Override
            public void close() {
                response.close();
            }
        };
    }

    private byte[] parse(InputStream is, String uri) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
        String line = "";
        StringBuilder sb= new StringBuilder();
        while((line=br.readLine())!=null){
            sb.append(line);
        }
        String responseStr = sb.toString();
        log.info("{} response: {}", uri, responseStr);
        // result为空,则不需要再做解密处理
        if(!responseStr.contains("\"result\":")) return null;

        Result resResult = JSONObject.parseObject(responseStr, Result.class);
        Object resResultObj = resResult.getData();
        String resResultStr = null;
        if(resResultObj instanceof JSONObject){
            resResultStr = ((JSONObject)resResultObj).toJSONString();
        }else{
            resResultStr = (String)resResultObj;
        }
        byte[] decryptBytes = rsa.decrypt(resResultStr, KeyType.PublicKey);
        String decryptStr = StrUtil.str(decryptBytes, CharsetUtil.CHARSET_UTF_8);
        resResult.setData(decryptStr);
        String decryptResult = JSON.toJSONString(resResult);
        return decryptResult.getBytes(StandardCharsets.UTF_8);
    }
}
